#pragma once

#include <string>
#include <vector>
#include <list>

#include <tr1/unordered_set>

#include <scip/scip.h>
#include <scip/scipdefplugins.h>

using namespace std;
using namespace std::tr1;

namespace ilp {

  const int CT_NO_MATTER   = 9999;
  
  typedef SCIP_VAR*                     variable_t;
  typedef SCIP_CONS*                    constraint_t;
  typedef SCIP_SOL*                     solution_t;

  const variable_t INVALID_HANDLE = NULL;
  
  typedef unordered_set<variable_t> variable_set_t;
  
  struct constraint_template_t {
  
    vector<variable_t> variables;
    vector<double>         coefficients;
    int                    lhs, rhs;
    
    constraint_template_t( int _lhs, int _rhs ) {
      lhs = _lhs; rhs = _rhs;
    }
  
    constraint_template_t( int _lhs, int _rhs, vector<variable_t> &_vars ) {
      lhs = _lhs; rhs = _rhs;
      variables = _vars;
      coefficients = vector<double>( _vars.size(), 1.0 );
    }
  
    constraint_template_t( int _lhs, int _rhs, variable_set_t &_vars ) {
      lhs = _lhs; rhs = _rhs;

      for( variable_set_t::iterator iter_v = _vars.begin(); _vars.end() != iter_v; ++iter_v )
        push_back( *iter_v, 1.0 );
      
    }
  
    constraint_template_t( int _lhs, int _rhs, int _num_vars, ... ) {
      lhs = _lhs; rhs = _rhs;

      va_list lhs_vars;
      va_start( lhs_vars, _num_vars );

      int     _orig_num_vars = _num_vars;
    
      while( _num_vars-- > 0 ) {
        variable_t var = va_arg( lhs_vars, variable_t );
        variables.push_back( var );
      }

      _num_vars = _orig_num_vars;
    
      while( _num_vars-- > 0 ) {
        double coe = va_arg( lhs_vars, double );
        coefficients.push_back ( coe );
      }
      
      va_end( lhs_vars );
      
    }

    inline void push_back( variable_t var, double coe ) {
      variables.push_back( var );
      coefficients.push_back( coe );
    }
  
  };
  
  class solver_t {
    SCIP                  *m_p_scip;
    
    list<variable_t>   m_variables;
    list<constraint_t> m_constraints;
    vector<solution_t> m_solutions;
        
  public:

    solver_t();
    ~solver_t();

    void flush() {};
    void solve();
    void release();

    void                   add_constraint( constraint_template_t& );
    variable_t             add_variable( double );
    void                   del_variable( variable_t var );
    
    inline void            fix_variable( variable_t var, double val ) { SCIP_Bool f_infeasible, f_fixed; SCIPfixVar( m_p_scip, var, val, &f_infeasible, &f_fixed ); };

    inline void            set_timeout( double ) {};
    inline void            set_threads( int ) {};
    inline int             get_solution_status() { return -1; };
    
    inline double          get_objfn() { return SCIPgetSolOrigObj( m_p_scip, m_solutions[0] ); };
    inline double          get_variable_val( variable_t var ) { return SCIPgetSolVal( m_p_scip, m_solutions[0], var ); };
    inline void            set_variable_obj( variable_t var, double coefficient ) { SCIPchgVarObj( m_p_scip, var, coefficient ); };

    inline list<variable_t> &get_variables() { return m_variables; };
    inline list<constraint_t> &get_constraints() { return m_constraints; };
    inline vector<solution_t> get_solutions() { return m_solutions; };
    
  };

  
};
